[% setvar title Objects : Core support for method delegation %]
<div id="archive-notice">
    <h3>This file is part of the Perl 6 Archive</h3>
    <p>To see what is currently happening visit <a href="http://www.perl6.org/">http://www.perl6.org/</a></p>
</div>
<div class='pod'>
<a name='TITLE'></a><h1>TITLE</h1>
<p>Objects : Core support for method delegation</p>
<a name='VERSION'></a><h1>VERSION</h1>
<pre>  Maintainer: Damian Conway &lt;<a href='mailto:damian@conway.org'>damian@conway.org</a>&gt;
  Date: 4 Sep 2000
  Last Modified: 25 Sep 2000
  Mailing List: <a href='mailto:perl6-language-objects@perl.org'>perl6-language-objects@perl.org</a>
  Number: 193
  Version: 2
  Status: Frozen</pre>
<a name='ABSTRACT'></a><h1>ABSTRACT</h1>
<p>This RFC proposes that Perl 6 offer built-in support (via a pragma) for
delegating method calls to attributes of an object.</p>
<a name='DESCRIPTION'></a><h1>DESCRIPTION</h1>
<p>Delegation of method calls to attributes is a powerful OO technique that
is not well supported in most OO-capable languages, including Perl 5.</p>
<p>Delegation offers most of the advantages of inheritance (and, more
particularly, multiple inheritance) without most of the headaches. It
also offers some extra features that inheritance cannot provide.</p>
<p>The proposed delegation mechanism would work via a pragma:</p>
<pre>	use delegation
		attr1 =&gt; [qw( method1 method2 method3 )],
		attr2 =&gt; [qw( method4 method5 )],
		attr3 =&gt; __ALL__,
		attr4 =&gt; __ALL__,
		# etc.
		;</pre>
<p>This would cause method calls whose names match an element in the first
list to be delegated to the &quot;attr1&quot; attribute of an object. Likewise,
calls to a method whose name appears in the second list would be
forwarded to the &quot;attr2&quot; attribute of the object.</p>
<p>That is, calls like:</p>
<pre>        $obj-&gt;method3(@args);
        $obj-&gt;method5(@other_args);</pre>
<p>would act as if they were:</p>
<pre>        $obj-&gt;{attr1}-&gt;method3(@args);
        $obj-&gt;{attr2}-&gt;method5(@other_args);</pre>
<p>(and, if these attribute objects also delegated, the process might repeat
recursively until some deeply nested attribute actually provided a method
to call).</p>
<p>Attributes which appear with the string &quot;__ALL__&quot; instead of a
method list become &quot;catch-alls&quot;. Unresolvable method calls are delegated
to the first of these that is able to handle it.</p>
<p>So, for example, a call like:</p>
<pre>        $obj-&gt;method6(@more_args);</pre>
<p>would become equivalent to:</p>
<pre>        $obj-&gt;{attr3}-&gt;method6(@more_args);</pre>
<p>if $obj-&gt;{attr3} had a <code>method6</code> method (or an <code>AUTOLOAD</code>), or else:</p>
<pre>        $obj-&gt;{attr4}-&gt;method6(@more_args);</pre>
<p>if $obj-&gt;{attr4} had a suitable method.</p>
<p>Unlike explicitly delegated methods, which are delegated on the first
method look-up pass, delegation to catch-alls occurs on the second pass,
just before the dispatch mechanism tries the package's <code>AUTOLOAD</code> method.</p>
<p>Note that the presence of one or more catch-all's does not prevent an
<code>AUTOLOAD</code> being called, if none of the catch-alls can handle the
requested method.</p>
<p>If the explicit methods, the catch-alls (and any <code>AUTOLOAD</code>) all fail to
provide a suitable method, the normal dispatch would then continue into the
object's ancestral classes (if any).</p>
<p>An attribute can appear several times in a <code>use delegation</code> statement, with
all its delegation method lists being consolidated.</p>
<p>An attribute may also appear both with an explicit delegation list and as a
catch-all. For example:</p>
<pre>        use delegation
                attr1 =&gt; [qw(method1 method2)],
                attr2 =&gt; [qw(method3 method4)],
                attr1 =&gt; __ALL__,
                ;</pre>
<p>This example specifies that calls to the methods <code>method1</code> and
<code>method2</code> should be delegated to the &quot;attr1&quot; attribute, calls to the
methods <code>method3</code> and <code>method4</code> should be delegated to the &quot;attr2&quot;
attribute, and any remaining calls that are not handled before the
<code>AUTOLOAD</code> pass should be delegated to the &quot;attr1&quot; attribute (if
it can handle them).</p>
<a name='New dispatch sequence'></a><h2>New dispatch sequence</h2>
<p>With delegation available, the method dispatch sequence becomes
(<i>changes from Perl 5 mechanism in italics</i>):</p>
<ul>
<li><a name='1.'></a>1.</li>
<p>Look for the named method in current class, <i>checking for explicitly
delegated methods as well</i>.</p>
<li><a name='2.'></a>2.</li>
<p>Recursively repeat steps 1 and 2 in ancestral class(es) and in UNIVERSAL.</p>
<li><a name='3.'></a>3.</li>
<p><i>Check for implicitly delegated methods through catch-all attributes.
These may be explicit or <code>AUTOLOAD</code>ed in the attribute object's class.</i></p>
<li><a name='4.'></a>4.</li>
<p>Check for <code>AUTOLOAD</code> in current class</p>
<li><a name='5.'></a>5.</li>
<p>Recursively repeat steps 3 to 5 in ancestral class(es) and in UNIVERSAL.</p>
</ul>
<a name='Using delegation instead of inheritance'></a><h2>Using delegation instead of inheritance</h2>
<p>One powerful application of delegation is as a replacement for inheritance
where the internals of a prospective base class are inaccessible or
inconvenient, or the base class was not designed to be inherited and yet
it must be.</p>
<p>For example, consider the task of creating an IO::File-like class that
reads and writes to separate handles:</p>
<pre>        use IO::Bi;

        my $handle = IO::Bi-&gt;new('infile', 'outfile');

        if (defined($_ = $handle-&gt;getline)) {
                $handle-&gt;print($_);
        }

        foreach ($handle-&gt;getlines) {
                $handle-&gt;print($_);
        }</pre>
<p>IO::Bi can't inherit from IO::File, because it needs two file handles,
with input methods going to one and output methods going to the other.
That's impossible with inheritance (even using the dreaded &quot;diamond
inheritance pattern&quot;) because a class can inherit the state of any
ancestral class only once, no matter how many paths that ancestor is
inherited through. In C++ terms, all inheritance in Perl is &quot;virtual&quot;.</p>
<p>With delegation, the solution is trivial:</p>
<pre>        package IO::Bi;
        use IO::File;

        sub new {
                my ($class, $infile, $outfile) = @_;
                bless {
                        in  =&gt; IO::File-&gt;new($infile) || die,
                        out =&gt; IO::File-&gt;new(&quot;&gt; $outfile&quot;) || die,
                }, $class;
        }

        use delegation
                in  =&gt; [qw( getline getlines getc ungetc eof read sysread
                           input_record_separator input_line_number )],
                out =&gt; __ALL__,
                ;

        sub error {
                my ($self) = @_;
                return $self-&gt;{in}-&gt;error || $self-&gt;{out}-&gt;error;
        }

        sub opened {
                my ($self) = @_;
                return $self-&gt;{in}-&gt;opened &amp;&amp; $self-&gt;{out}-&gt;opened;
        }</pre>
<p>Here, all the input-specific methods are passed to the filehandle
stored in the &quot;in&quot; attribute. Everything else goes to the &quot;out&quot; attribute's
filehandle.</p>
<p>Note that the class can still define explicit methods like <code>error</code> and
<code>opened</code>, to catch cases where the method should be delegated to
<i>both</i> attributes.</p>
<a name='Replacing multiple inheritance'></a><h2>Replacing multiple inheritance</h2>
<p>Another situation where delegation is useful is where it is necessary to
inherit from two classes that are both implemented via pseudohashes.
Multiple inheritance of pseudohashes is impossible (for good reasons),
but delegation gives almost the same effect:</p>
<pre>        package PseudoMI;

        use fields qw(ancestor1 ancestor2);
        use delegation (ancestor1 =&gt; __ALL__, ancestor2 =&gt; __ALL__);

        sub new {
                my PseudoMI $self = fields::new($_[0]);
                $self-&gt;{ancestor1} = PseudoBase1-&gt;new();
                $self-&gt;{ancestor2} = PseudoBase2-&gt;new();
                return $self;
        }</pre>
<a name='Delegation to avoid inheritance'></a><h2>Delegation to avoid inheritance</h2>
<p>Delegation is also powerful where inheritance is possible, but
inconvenient because the base class is not designed to be inherited, or
inheritance semantics (such as automagic base class dtor calls) are
undesirable:</p>
<pre>        package File::Lock::Mac;
        use File::Lock;
        use delegation  uninherited_but_used =&gt; __ALL__;

        sub new {
                my $class = shift @_;
                bless { uninherited_but_used =&gt; File::Lock-&gt;new(@_) }, $class;
        }

        use Mac::Files;

        sub lock {
                my ($self) = @_;
                Mac::Files::FSpSetFLock($self-&gt;filename);
        }

        sub DESTROY {
                my ($self) = @_;
                Mac::Files::FSpRstFLock($self-&gt;filename);
        }</pre>
<a name='Delegation for sanitization'></a><h2>Delegation for sanitization</h2>
<p>Delegation also provides a mechanism for upgrading legacy code to take
advantage of new OO features. Consider the encapsulated (private) hash entries
proposed by RFC 188. These provide a simple mechanism for strong
encapsulation but are only applicable to hashes.</p>
<p>But, with delegation, a legacy base class could be integrated into a
Perl 6 class that is based on encapsulated hashes, regardless of how the legacy
class itself is implemented.</p>
<p>Consider extending a Person class (with objects implemented as, say,
blessed subroutines) with dog-tag information (to be implemented using
privatized hashes):</p>
<pre>        package DogTag;
        use Person;

        sub new {
                my ($class, $name, $rank, $snum) = @_;
                bless private {
                        name =&gt; Person-&gt;new($name),
                        rank =&gt; $rank,
                        snum =&gt; $snum,
                }, $class;
        }

        sub rank {
                my ($self, $newval) = @_;
                $self-&gt;{rank} = $newval if @_ &gt; 1;
                return $self-&gt;{rank};
        }

        sub serial_num {
                my ($self, $newval) = @_;
                $self-&gt;{snum} = $newval if @_ &gt; 1;
                return $self-&gt;{snum};
        }

        use delegation  name =&gt; __ALL__;</pre>
<p>Note that the DogTag class is completely insulated from the implementation
details of the Person class. The above code would be identical if
Person objects were hashes, arrays, pseudohashes, flyweight scalars,
blessed regexes or typeglobs, or even some arcane XS-generated C struct.</p>
<p>Yet from outside the DogTag class, the &quot;name&quot; data is encapsulated in
exactly the same way as the rank and serial number information. And the
methods of the Person class are directly usable on DogTag objects, just
as if they had been inherited.</p>
<a name='MIGRATION ISSUES'></a><h1>MIGRATION ISSUES</h1>
<p>It is envisaged that delegation -- particularly the &quot;sanitization&quot;
technique shown in the last example above -- would provide an important
bridging mechanism for migrating existing Perl 5 OO class hierarchies to
the new features and functionalities of Perl 6.</p>
<p>Of itself, the pragma would break no existing code.</p>
<a name='IMPLEMENTATION'></a><h1>IMPLEMENTATION</h1>
<p>A module named Class::Delegation will soon appear on the CPAN.</p>
<p>It implements almost this entire proposal, except that it is forced to
use $self-&gt;SUPER::AUTOLOAD(), rather than $self-&gt;NEXT::AUTOLOAD(), in
cases where delegation fails. This prevents the module from achieving
the full transparency that the pragma would offer.</p>
<p>The module's delegation speed is also very slow compared with what an
built-in pragma would allow the interpreter to achieve.</p>
<a name='REFERENCES'></a><h1>REFERENCES</h1>
<p>RFC 137: Overview : Perl OO should <i>not</i> be fundamentally changed.</p>
<p>RFC 188: Objects : Private keys and methods</p>
<p>RFC 189: Objects : Hierarchical calls to initializers and destructors</p>
<p>RFC 190: Objects : NEXT pseudoclass for method redispatch</p>
<p>Sean M. Burke's Class::Classless module.</p>
<p>The Self progamming language &lt;<a href='http://www.objs.com/x3h7/self.htm' target='_blank'>www.objs.com</a>&gt;</p>
<p>The Objective-C progamming language</p>
<p>The Cecil progamming language</p>
<p>The Scheme progamming language</p>
<p>The Smalltalk-80 progamming language</p>
<p><a href='http://www-xdiv.lanl.gov/XCI/srlee/SOFTWARE/GENERAL/OOFAQ/oo-faq-S-3.2.html' target='_blank'>www-xdiv.lanl.gov</a>#S-3.2</p>
<p><a href='http://www.cbuildermag.com/features/2000/05/cb200005gm_f/cb200005gm_f.asp' target='_blank'>www.cbuildermag.com</a></p>
<p>Lynn Andea Stein, &quot;Delegation is Inhertance&quot;, Proc. OOPSLA '87,
Orlando, FL, October 4-8, 1977, pp. 138-146</p>
</div>
